/*
 * Created on Nov 27, 2005
 */
package dbsucxentW.pathProcessorEdgeW;

import java.sql.Connection;

import dbsucxentW.pathProcessorW.NoSuchPathException;
import dbsucxentW.constantW.EdgeConstant;	//klarinda 20070525

/**
 * @author Seah Boon Siew 
 * modified by Klarinda to change the algorithm
 * 20070625: change the code to handle more variaty of XPath (make it similar to sucxent's translator)
 */
public class EdgeTranslator {
	String _xPath;
	String _fromSQL;
	boolean _updated;
	CurrentPath _currentPath;
	//klarinda 20060828, not used WhereSQL _whereSQL;
	Connection _dbConnection;
	boolean _singleDoc;
	boolean _showPath;		// to include path of every leaf
	
	boolean _canCombine;
	String _sqlQuery;
	int _noOfTables;
	private boolean _isPrefetching = false;				//20070626
	
	public EdgeTranslator(String xPath, Connection conn, boolean single, boolean showPath){
		_xPath = xPath;
		_fromSQL = "";
		//_updated = false;
		_dbConnection = conn;
		_currentPath = new CurrentPath(_dbConnection);
		_singleDoc = single;
		_showPath = showPath;
		//_whereSQL = new WhereSQL(_singleDoc, _showPath);
		
		_canCombine = true;
		_sqlQuery = "";
		_noOfTables = 1;
	}
	
	public String translate()  throws NoSuchPathException {
		System.out.println( "EdgeTranslator.translate(), XPath: "+_xPath);		//klarinda 20060824

		// 20070625: copy & paste the string manipulation part from sucxent's pathprocessor
    	// Given an XPath, do a string manipulation to get each step, then call processStep for each step
    	// for example: /a/b[c/d = 'TCP/IP']/e is separated into: a, b[c/d = 'TCP/IP'], e
    	// (ideally W3C XPath parser should be used)
    	
    	String[] tempresult = _xPath.split("/");	// separate XPath according to "/"
		
		
    	//To handle "/" in the middle of the string (klarinda 20061114) 
    	//ex: /a/b[title='TCP/IP Illustrated'] --> split("/") will return "title='TCP" & "IP Illustrated'"
    	//so need to combine "title='TCP" + "/" + "IP Illustrated'"
    	int tempresult_length = tempresult.length;
        boolean quote = false, doublequote = false, stringliteral = false;	//20061123
        
    	for (int p=0; p<tempresult_length; p++){
    		quote = false; doublequote = false; stringliteral = false;
//    		System.out.println(p+": "+tempresult[p]);
    		for (int q=0; q<tempresult[p].length(); q++){
    			if (!stringliteral) {
	    			if (tempresult[p].charAt(q)=='\''){
        				quote = true; stringliteral = true;
	    			}
	    			else if (tempresult[p].charAt(q)=='"'){
        				doublequote = true; stringliteral = true;
	    			}
	    		}
    			else {
	    			if (quote && tempresult[p].charAt(q)=='\''){
        				quote = false; stringliteral = false;
	    			}
	    			else if (doublequote && tempresult[p].charAt(q)=='"'){
        				doublequote = false; stringliteral = false;
	    			}
    			}
    		}
    		if(stringliteral){
    			tempresult_length--;
    			tempresult[p]+="/" + tempresult[p+1];
//				System.out.println(p+": After concat: "+tempresult[p]);
    			for(int r=p+1; r<tempresult_length; r++){
    				tempresult[r]=tempresult[r+1];	//shift left operation
    			}
    		}
    	}
    	
    	//To handle predicate with > 1 steps, ex: /bib/book[author/last = 'Stevens'] (k 20061120)
    	boolean openbracket = false;
    	for (int p=0; p<tempresult_length; p++){
//    		System.out.println(p+": "+tempresult[p]);
    		for (int q=0; q<tempresult[p].length(); q++){
    			if (tempresult[p].charAt(q)=='['){
    				openbracket = true;
    			}
    			else if (tempresult[p].charAt(q)==']'){
    				openbracket = false;
    			} 
    		}
    		if(openbracket){
    			tempresult_length--;
    			tempresult[p]+="/" + tempresult[p+1];
//				System.out.println(p+": After concat: "+tempresult[p]);
    			for(int r=p+1; r<tempresult_length; r++){
    				tempresult[r]=tempresult[r+1];	//shift left operation
    			}
    			p--; //continue loop until found closebracket (openbracket=false)
    		}
    	}
    	
    	//create a new array for the result
    	String[] result = new String[tempresult_length];
    	for(int p=0; p<tempresult_length; p++){
    		result[p] = tempresult[p];
    	}

       	
	    for (int x=1; x<result.length; x++){	//start from 1 instead of 0
	    	result[x] = result[x].trim();		//trim leading and trailing space from the xpath
	    	System.out.println( "\n\nTranslator.translate(), result["+x+"] = "+result[x]);		//klarinda 20060824
	    	//if (x == 0){ 							// first step
	    	if (result.length == 2)					// there is only one step (first=last)
	    		processStep(result[x],true,true);
	    	else if (x == 1){ 						// first step
	    		processStep(result[x],true,false);
	    	} else if (x == result.length - 1){ 	// last step
	    		processStep(result[x],false,true);
	    	} else { 								// others
	    		processStep(result[x],false,false);
	    	}
	    }
	    
	    	    
       // join with Path table to obtain paths
       //if (_showPath)
       		//_whereSQL.addPaths();
       
	    /* 20060828 try to separate attribute table
       _sqlQuery += "SELECT E.* \n" +
       		"FROM Edge E, Q"+_noOfTables+" Q \n" +
       		"WHERE E.id >= Q.id AND E.id <= Q.end_desc_id \n" +
       		"ORDER BY Q.id, E.id \n";
       */
	   //klarinda 20060828 try to separate attribute table
	   /*klarinda 20060901 use temp table so that can use Foreign Key relationship between edge and attr
       _sqlQuery += "SELECT Q.id as qid, E.docid, E.id as id, E.parent_id, E.path_id, E.end_desc_id, E.value \n" +
    		"FROM Edge E, Q"+_noOfTables+" Q \n" + 
    		"WHERE E.id >= Q.id AND E.id <= Q.end_desc_id \n"+ 
    		"UNION ALL \n" +
    		"SELECT Q.id as qid, A.docid, A.parent_id as id, A.parent_id, A.path_id, A.parent_id as end_desc_id, A.value \n" +
    		"FROM Attribute A, Q"+_noOfTables+" Q \n"+
    		"WHERE A.parent_id >= Q.id AND A.parent_id <= Q.end_desc_id \n"+
    		"ORDER BY qid, id \n";
	   */
	    //20060905 add Q.docid (qdid) coz we need to include docid so that we can use clustered index
	    //20060905 add ORDER BY attr
	   _sqlQuery += ", QX (qdid, qid, docid, id, parent_id, path_id, end_desc_id, value ) AS ( \n" + 
				"    SELECT Q.docid as qdid, Q.id as qid, E.docid, E.id as id, E.parent_id, E.path_id, E.end_desc_id, E.value \n" + 
				"    FROM Edge E, Q"+_noOfTables+" Q  \n" +
				"    WHERE E.docid = Q.docid AND E.id >= Q.id AND E.id <= Q.end_desc_id \n" +  
				") \n" +
				"SELECT QX.*, 1 AS Attr \n" +
				"FROM QX \n" +
				"UNION ALL  \n" +
				"SELECT Q.docid as qdid, Q.qid as qid, A.docid, A.parent_id as id, A.parent_id, A.path_id, A.parent_id as end_desc_id, A.value, 0 AS Attr \n" + 
				"FROM Attribute A, QX Q  \n" +
				"WHERE A.docid = Q.docid AND A.parent_id = Q.id  \n" +
				"ORDER BY qdid, qid, docid, id, Attr  \n" ;
       
	  	//return _whereSQL.get();    
       System.out.println( "EdgeTranslator.translate(), sqlQuery:\n"+_sqlQuery);		//klarinda 20060825
       return _sqlQuery;
	}

	//klarinda 20070625 - copied from Sucxent.
	//given an XPath step, return the Step class (Step class contains the summary for each step)
	//this class contains some string manipulation
	//ideally W3C XPath Parser should be used 
	private Step getStep(String splice) throws NoSuchPathException {
		String axis, nameTest=null, predicates = null; 
		String[] predicate = {" "}, equalityOperator = null, equality = null;
		boolean hasPredicate = false;
		boolean[] hasEquality = {false};

		// split axis and predicate
        String[] parts = splice.split("::");
        
        // axis default?
        if (parts.length == 1){
        	//klarinda 20061103 add if @ -> attribute
        	if (parts[0].charAt(0) == '@')
        		axis = "attribute";
        	else
        		axis = "child";
        	predicates = parts[0];
        } else if(parts.length == 2) {
        	axis = parts[0];
        	predicates = parts[1];
        } else {
			throw new NoSuchPathException("Too many axis, axis undetermined");
        }

    	System.out.println( "Translator.getStep(), axis="+axis+", predicates="+predicates);	//klarinda 20060629
        
        // split predicate into nameTest and position based predicate(if any)
        String[] parts2 = predicates.split("[\\[\\]]");
        
        for (int k=0; k<parts2.length; k++) {	//klarinda 20060629
        	System.out.println( "Translator.getStep(), parts2["+k+"]="+parts2[k]);	//klarinda 20060629
        	parts2[k] = parts2[k].trim();		
        }
        
        //klarinda 20061122 string manipulation to separate based on "and", "or", "(", or ")"
        //for example, the predicate: [title="abc" and ( ( publisher='xyz' and price>400) or position()= 2 to 4   )]
        //i and   or    ( "expr" 					)	 
        //0 false false 0 "title="abc"" 			0
        //1 true  false 2 "publisher='xyz'" 		0
        //2 true  false 0 "price>400" 				1
        //3 false true  0 "position()= 2 to 4" 		1
        String[] 	expr 					= new String[20];	//assume 20 is max
        int[] 		noOfOpenParenthesis 	= new int[20];
        int[] 		noOfCloseParenthesis 	= new int[20];
        boolean[] 	andExpr 				= new boolean[20];
        boolean[] 	orExpr 					= new boolean[20];
        
        int state = 1, pos = 0, begin = pos, exprIndex = 0;
        boolean quote = false, doublequote = false, stringliteral = false, functioncall=false;
        String pred = "";
        if(parts2.length > 1)
        	pred = parts2[1];
        while (pos<pred.length()) {
//        	System.out.println(pos+" "+pred.charAt(pos)+" "+state);
        	if (state == 1) {			//look for open parenthesis '('
        		if (pred.charAt(pos) == ' ');			//white space - do nothing
        		else if (pred.charAt(pos) == '(') {		//open parenthesis '('
        			noOfOpenParenthesis[exprIndex]++;
        		}
        		else {									//start of expression
//        			System.out.println("\nstate 1 -> state 2");
        			state++;								//go to the next state
        			quote = false; doublequote = false; 	//initialization for the next state
        			stringliteral=false; functioncall=false;
        			begin = pos;
        			pos--;		//need to minus because the pos will be incremented at the end of the if-else blk
        		}
        	}
        	else if (state == 2) {		//expression, ex: title = 'abc', 2 to 4, ...	
        		if (!stringliteral) {
        			if (pred.charAt(pos) == '\'') {
        				quote = true; stringliteral = true;
        			}
        			else if (pred.charAt(pos) == '"') {
        				doublequote = true; stringliteral = true;
        			}
        			else if (pred.charAt(pos) == '(') {
        				functioncall=true;
        			}
        			else if (pred.charAt(pos) == ')' && functioncall) {
        				functioncall=false;
        			}
        			//look for the end of the expression
        			//the expr is terminated with close parenthesis or "and"/"or" or reaching the end
        			else if (pred.charAt(pos) == ')' && !functioncall) {	
//            			System.out.println("encounter ), begin: "+begin+", pos: "+pos);
        				expr[exprIndex] = pred.substring(begin, pos).trim();
//        				System.out.println("exprIndex, expr: "+exprIndex+" "+expr[exprIndex]);
            			exprIndex++;
        				state++; pos--;
//            			System.out.println("state 2 -> state 3");
        			}
        			else if (pos==pred.length()-1) {
//            			System.out.println("Last char of the predicate, begin: "+begin+", pos: "+pos);
        				expr[exprIndex] = pred.substring(begin, pos+1).trim();
//        				System.out.println("exprIndex, expr: "+exprIndex+" "+expr[exprIndex]);
            			exprIndex++;
        			}
        			else if (pos+5<pred.length() && pred.substring(pos, pos+5).compareTo(" and ")==0) {
//            			System.out.println("encounter and, begin: "+begin+", pos: "+pos);
        				expr[exprIndex] = pred.substring(begin, pos).trim();
//        				System.out.println("exprIndex, expr: "+exprIndex+" "+expr[exprIndex]);
            			exprIndex++;
        				andExpr[exprIndex] = true;
        				pos=pos+4;
        				state=1;
//            			System.out.println("state 2 -> state 1");
        			}
        			else if (pos+4<pred.length() && pred.substring(pos, pos+4).compareTo(" or ") ==0 ) {
//            			System.out.println("encounter or, begin: "+begin+", pos: "+pos);
        				expr[exprIndex] = pred.substring(begin, pos).trim();
//        				System.out.println("exprIndex, expr: "+exprIndex+" "+expr[exprIndex]);
            			exprIndex++;
        				orExpr[exprIndex] = true;
        				pos=pos+3;
        				state=1;
//            			System.out.println("state 2 -> state 1");
        			}
        		}
        		else {
        			if (quote && pred.charAt(pos)=='\'') {
        				quote = false; stringliteral = false;
        				if (pos==pred.length()-1) {
//                			System.out.println("Last char of of predicate, begin: "+begin+", pos: "+pos);
            				expr[exprIndex] = pred.substring(begin, pos+1).trim();
//            				System.out.println("exprIndex, expr: "+exprIndex+" "+expr[exprIndex]);
                			exprIndex++;
        				}
        			}
        			else if (doublequote && pred.charAt(pos)=='"') {
        				doublequote = false; stringliteral = false;
        				if (pos==pred.length()-1) {
//                			System.out.println("Last char of predicate, begin: "+begin+", pos: "+pos);
            				expr[exprIndex] = pred.substring(begin, pos+1).trim();
//            				System.out.println("exprIndex, expr: "+exprIndex+" "+expr[exprIndex]);
                			exprIndex++;
        				}
        			}
        		}
        	}
        	else if (state == 3) {			//look for close parenthesis ')'
        		if (pred.charAt(pos) == ' ');			//white space - do nothing
        		else if (pred.charAt(pos) == ')') {		//close parenthesis ')'
        			noOfCloseParenthesis[exprIndex-1]++;
        		}
        		else {									//start of expression
        			state++;		//go to the next state
        			pos--;
//        			System.out.println("\nstate 3 -> state 4");
        		}
        	}
        	else if (state == 4) {
    			if (pred.substring(pos, pos+3).compareTo("and")==0 ){
    				andExpr[exprIndex] = true;
    				pos++; pos++;
    			}
    			else if (pred.substring(pos, pos+2).compareTo("or")==0) {
    				orExpr[exprIndex] = true;
    				pos++;
    			}
    			state = 1;
    			System.out.println("\nstate 4 -> state 1");
        	}
        	else {
        		throw new NoSuchPathException("Invalid state!");
        	}
        	pos++;
        }
        System.out.println("\ni and or ( Expr )");
        for (int i=0; i<exprIndex; i++) {
        	System.out.println(i +" "+ andExpr[i] +" "+ orExpr[i] +
        			" "+noOfOpenParenthesis[i] +" \""+ expr[i] +"\" "+ noOfCloseParenthesis[i]);
        }
        System.out.println("\n");
        	
        //end of klarinda
		
	    if (parts2.length == 1){
        	nameTest = parts2[0];
    		String[] temp = {" "};         
        	predicate = temp;
        	hasPredicate = false;                	
        } else if (parts2.length > 1){
        	nameTest = parts2[0];
        	//predicate = parts2[1];
        	hasPredicate = true;
        	
        	//actually the name predicate supposed to be changed to expression. 
        	//but there are too many parts need to be changed because of this. 
        	//so i just keep the original
        	predicate = new String[exprIndex];
        	for(int i=0; i<exprIndex; i++){
        		predicate[i] = expr[i];
        	}
        	equality = new String[predicate.length];
        	hasEquality = new boolean[predicate.length];
        	equalityOperator = new String[predicate.length];
        	/* klarinda 20061114
        	predicate = new String[parts2.length/2];
        	equality = new String[parts2.length/2];
        	hasEquality = new boolean[parts2.length/2];
        	equalityOperator = new String[parts2.length/2];
        	*/
        	
	    	for (int i = 0; i < predicate.length; i++) {
	    		hasEquality[i] = false;
	        	equalityOperator[i] = "";
	        	equality[i] = "";
	        	
	    		//klarinda 20061114 predicate[i] = parts2[i*2+1];
	    	
	        	// search for equality component, if any
	        	String[] equalityParts = predicate[i].split("!=");
	        	if (equalityParts.length > 1){
	        		equalityOperator[i] = "!=";
	        		predicate[i] = equalityParts[0];
	        		equality[i] = equalityParts[1];    
	        		hasEquality[i] = true;
	        	} else {
	        	equalityParts = predicate[i].split("<=");
	        	}
	        	if (equalityParts.length > 1 && !hasEquality[i]){
	        		equalityOperator[i] = "<=";
	        		predicate[i] = equalityParts[0];
	        		equality[i] = equalityParts[1];    
	        		hasEquality[i] = true;
	        	} else {
	            	equalityParts = predicate[i].split(">=");
	        	}
	        	if (equalityParts.length > 1 && !hasEquality[i]){
	        		equalityOperator[i] = ">=";
	        		predicate[i] = equalityParts[0];
	        		equality[i] = equalityParts[1];    
	        		hasEquality[i] = true;
	        	} else {
	            	equalityParts = predicate[i].split("=");
	        	}
	        	if (equalityParts.length > 1 && !hasEquality[i]){
	        		//klarinda 20061114 for position()
	        		if(equalityParts[0].trim().equals("position()")){
	        			System.out.println("position()");
	        			predicate[i] = equalityParts[1];
	        			equalityParts = predicate[i].split("<");
	        		}
	        		else{
		        		equalityOperator[i] = "=";
		        		predicate[i] = equalityParts[0];
		        		equality[i] = equalityParts[1];
		        		//SQL can't accept ", can only accept '
		        		//SQL need to escape ', ex: Smith's --> Smith''s
		        		String temp = equalityParts[1].trim();
		        		if (temp.charAt(0) == '\'' || temp.charAt(0) == '"')		//string literal (20061206)
		        			equality[i] = "'" + temp.substring(1,temp.length()-1).replaceAll("'","''") + "'";
		        		else
		        			equality[i] = temp;
		        		hasEquality[i] = true;
	        		}
	        	} else {
	            	equalityParts = predicate[i].split("<");
	        	}
	        	if (equalityParts.length > 1 && !hasEquality[i]){
	        		equalityOperator[i] = "<";
	        		predicate[i] = equalityParts[0];
	        		equality[i] = equalityParts[1];    
	        		hasEquality[i] = true;
	        	} else {
	            	equalityParts = predicate[i].split(">");
	        	}
	        	if (equalityParts.length > 1 && !hasEquality[i]){
	        		equalityOperator[i] = ">";
	        		predicate[i] = equalityParts[0];
	        		equality[i] = equalityParts[1];    
	        		hasEquality[i] = true;
	        	} 
	        	
	        	//klarinda 20070108 for MINT query only
	        	else if (predicate[i].contains("!!!")) {
	        		//use any string to differentiate MINT query, no time to chg the whole algo
	            	equalityParts = predicate[i].split("!!!");
		        	if (equalityParts.length > 1 && !hasEquality[i]){
		        		equalityOperator[i] = "!!!";
		        		predicate[i] = equalityParts[0];
		        		equality[i] = equalityParts[1];    
		        		hasEquality[i] = true;
		        	} 
		        	else if (!hasEquality[i]) {
		        		equalityOperator[i] = "!!!";
		        		predicate[i] = equalityParts[0];
		        		hasEquality[i] = true;
		        	}
	        	}
	        	//end of klarinda 20070108
	        	
	        	predicate[i] = predicate[i].trim();	
	    	}        	
	        	
	    }
	    
	    //klarinda 20060629 - add comment for debugging purpose
	    System.out.println( "Translator.getStep(), createStep");
	    System.out.println( "   axis          : "+axis);			
	    System.out.println( "   nameTest      : "+nameTest);		
	    System.out.print  ( "   predicate     : ");					
	    if (predicate!=null)
		    for(int k=0;k<predicate.length;k++)
		    	System.out.print(predicate[k]+", ");
	    System.out.print("\n");
	    System.out.print  ( "   equality      : ");					
	    if (equality!=null)
		    for(int k=0;k<equality.length;k++)
		    	System.out.print(equality[k]+", ");
	    System.out.print("\n");
	    System.out.println( "   hasPredicate  : "+hasPredicate);	
	    System.out.print  ( "   hasEquality   : ");					
	    if (hasEquality!=null)
		    for(int k=0;k<hasEquality.length;k++)
		    	System.out.print(hasEquality[k]+", ");
	    System.out.print("\n");
	    System.out.print  ( "   equalityOp    : ");					
	    if (equalityOperator!=null)
		    for(int k=0;k<equalityOperator.length;k++)
		    	System.out.print(equalityOperator[k]+", ");
	    System.out.print("\n");
	    System.out.print  ( "   no of (       : ");					
	    if (predicate!=null)
		    for(int k=0;k<predicate.length;k++)
		    	System.out.print(noOfOpenParenthesis[k]+", ");
	    System.out.print("\n");
	    System.out.print  ( "   no of )       : ");					
	    if (predicate!=null)
		    for(int k=0;k<predicate.length;k++)
		    	System.out.print(noOfCloseParenthesis[k]+", ");
	    System.out.print("\n");
	    System.out.print  ( "   andExpr       : ");					
	    if (predicate!=null)
		    for(int k=0;k<predicate.length;k++)
		    	System.out.print(andExpr[k]+", ");
	    System.out.print("\n");
	    System.out.print  ( "   or Expr       : ");					
	    if (predicate!=null)
		    for(int k=0;k<predicate.length;k++)
		    	System.out.print(orExpr[k]+", ");
	    System.out.print("\n");
	    
        //create Step
	    //Step step = new Step(axis,nameTest,predicate, equality, hasPredicate, hasEquality, equalityOperator); //k20061122
	    Step step = new Step(axis,nameTest,predicate, equality, hasPredicate, hasEquality, equalityOperator,	//k20061122  
	    		noOfOpenParenthesis, noOfCloseParenthesis, andExpr, orExpr);	
        return step;
		
	}
	

	//process each step
	void processStep(String splice, boolean first, boolean last)  throws NoSuchPathException {
//		String axis, nameTest, predicate, equality = null;
//		String equalityOperator = null;//klarinda 20060824 to add other operators
//		boolean hasPredicate;
//		boolean hasEquality = false;
		System.out.println( "EdgeTranslator.processStep("+splice+","+first+","+last+")");		//klarinda 20060824
		         
    	Step step = getStep(splice);		//20070625 separate the string manipulation part to another function

        if (first){
        	/* combine several steps into one table instead of join of several tables
        	 * ex: the XPath is /a/b/c/following::d/e/f 
        	 *     /a/b/c can be combined; instead of joining 3 tables, just use one table using path_id
        	_sqlQuery = "WITH Q1 (id, parent_id, end_desc_id) AS ( \n";
        	_sqlQuery+= "    SELECT DISTINCT id, parent_id, end_desc_id \n";
        	_sqlQuery+= "    FROM Edge \n";
        	_sqlQuery+= "    WHERE parent_id = 0) \n";
        	*/
        	_currentPath.add(step.getNameTest());
    	} else {
    		//combine several steps into one table instead of join of several tables
    		if(_canCombine && step.getType()== EdgeConstant.CHILD && // step.hasPredicate() == false &&
    	        step.getNameTest().compareTo("*") != 0 && step.getNameTest().compareTo("node()") != 0) {
    			_currentPath.add(step.getNameTest());
    			//if (step.hasPredicate() || step.hasEquality() ) { //can no longer combine the steps
    			//20070625 chg Step class
    			if (step.hasPredicate()) { //can no longer combine the steps
    				//klarinda 20060906 add docid
       	        	_sqlQuery = "WITH Q1 (docid, id, parent_id, end_desc_id) AS ( \n";
       	        	_sqlQuery+= "    SELECT DISTINCT docid, id, parent_id, end_desc_id \n";
       	        	_sqlQuery+= "    FROM Edge \n";
   					String inpath = _currentPath.getActualPath();
   					if (inpath.equals("()"))
   						throw new NoSuchPathException("The path does not exist");
       	        	//_sqlQuery+= "    WHERE docid = 1 AND path_id in " + inpath +" ) \n"; 	//hardcode docid for experiment purpose
   					_sqlQuery+= "    WHERE path_id in " + inpath +" ) \n"; 					//remove hardcode
        			_canCombine = false;
    	        	processPredicate(step);
    			}
    		}
    		else {
    			if(_canCombine) {	//the first step where the step can no longer be combined
    				//20060906 add docid
    	        	_sqlQuery = "WITH Q1 (docid, id, parent_id, end_desc_id) AS ( \n";
    	        	_sqlQuery+= "    SELECT DISTINCT docid, id, parent_id, end_desc_id \n";
    	        	_sqlQuery+= "    FROM Edge \n";
					String inpath = _currentPath.getActualPath();
					if (inpath.equals("()"))
						throw new NoSuchPathException("The path does not exist");
    	        	//_sqlQuery+= "    WHERE docid = 1 AND path_id in " + inpath +" ) \n"; 	//hardcode docid for experiment purpose
					_sqlQuery+= "    WHERE path_id in " + inpath +" ) \n"; 					//remove hardcode
    			}
    			_canCombine = false;
	        	processAxis(step);
	        	//if (step.hasPredicate() || step.hasEquality()) processPredicate(step);
	        	if (step.hasPredicate()) processPredicate(step); //k 20070625 chg Step class
    		}
        //}
    	}
                
      	// last step finalization (update path to SQL if not updated)
        if (last && _canCombine){ //if all of the steps in the XPath can be combined into 1 table
        	//20060906 add docid
        	_sqlQuery = "WITH Q1 (docid, id, parent_id, end_desc_id) AS ( \n";
        	_sqlQuery+= "    SELECT DISTINCT docid, id, parent_id, end_desc_id \n";
        	_sqlQuery+= "    FROM Edge \n";
			String inpath = _currentPath.getActualPath();
			if (inpath.equals("()"))
				throw new NoSuchPathException("The path does not exist");
        	//_sqlQuery+= "    WHERE docid = 1 AND path_id in " + inpath +" ) \n"; 	//harcode docid for experiment
			_sqlQuery+= "    WHERE path_id in " + inpath +" ) \n"; 					//remove hardcode
        }
        
	}
	
	void processAxis(Step step) throws NoSuchPathException {
		System.out.println( "EdgeTranslator.processAxis()");		//klarinda 20060824
		
		_noOfTables++;		
		String sql = "";
			
		switch (step.getType()){
			case EdgeConstant.CHILD:		//child
				System.out.println( "EdgeTranslator.processAxis(), step: CHILD");		//klarinda 20060824
				//20060906 add docid
				sql = ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";
				sql+= "    SELECT DISTINCT E.docid, E.id, E.parent_id, E.end_desc_id \n";
				sql+= "    FROM Edge E, Q"+(_noOfTables-1)+" Q \n";
				sql+= "    WHERE E.docid = Q.docid AND E.parent_id = Q.id ";

				_currentPath.add(step.getNameTest());
				
				if (step.getNameTest().compareTo("*") == 0 || step.getNameTest().compareTo("node()") == 0){
					sql+=")\n";
				}
				else {
					String inpath = _currentPath.getActualPath();
					if (inpath.equals("()"))
						throw new NoSuchPathException("The path does not exist");
					sql+="AND E.path_id in " + inpath +")\n";
				} 
				break;
			case EdgeConstant.FOLLOWING:		//following
				System.out.println( "EdgeTranslator.processAxis(), step: FOLLOWING");		//klarinda 20060824
				//20060906 add docid
				sql = ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";
				sql+= "    SELECT DISTINCT E.docid, E.id, E.parent_id, E.end_desc_id \n";
				sql+= "    FROM Edge E, Q"+(_noOfTables-1)+" Q \n";
				sql+= "    WHERE E.docid = Q.docid AND E.id > Q.end_desc_id ";
				_currentPath.clearPath();
				_currentPath.add("*");
				
				if (step.getNameTest().compareTo("*") == 0 || step.getNameTest().compareTo("node()") == 0){
					sql+=")\n";
				}
				else {
					_currentPath.add(step.getNameTest());
					String inpath = _currentPath.getActualPath();
					if (inpath.equals("()"))
						throw new NoSuchPathException("The path does not exist");
					sql+="AND E.path_id in " + inpath +")\n";
				} 
				break;

			case EdgeConstant.PRECEDING:		//preceding
				System.out.println( "EdgeTranslator.processAxis(), step: PRECEDING");		//klarinda 20060824
				//20060906 add docid
				sql = ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";
				sql+= "    SELECT DISTINCT E.docid, E.id, E.parent_id, E.end_desc_id \n";
				sql+= "    FROM Edge E, Q"+(_noOfTables-1)+" Q \n";
				sql+= "    WHERE E.docid = Q.docid AND E.end_desc_id < Q.id ";
				_currentPath.clearPath();
				_currentPath.add("*");
				if (step.getNameTest().compareTo("*") == 0 || step.getNameTest().compareTo("node()") == 0){
					sql+=")\n";
				}
				else {
					_currentPath.add(step.getNameTest());
					String inpath = _currentPath.getActualPath();
					if (inpath.equals("()"))
						throw new NoSuchPathException("The path does not exist");
					sql+="AND E.path_id in " + inpath +")\n";
				} 
				break;

			case EdgeConstant.FOLLOWING_SIBLING:		//following-sibling
				System.out.println( "EdgeTranslator.processAxis(), step: FOLLOWING-SIBLING");	//klarinda 20060824
				//20060906 add docid
				sql = ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";
				sql+= "    SELECT DISTINCT E.docid, E.id, E.parent_id, E.end_desc_id \n";
				sql+= "    FROM Edge E, Q"+(_noOfTables-1)+" Q \n";
				sql+= "    WHERE E.docid = Q.docid AND E.id > Q.end_desc_id AND E.parent_id = Q.parent_id ";
				_currentPath.removeLast();
				_currentPath.add(step.getNameTest());
				if (step.getNameTest().compareTo("*") == 0 || step.getNameTest().compareTo("node()") == 0){
					sql+=")\n";
				}
				else {
					String inpath = _currentPath.getActualPath();
					if (inpath.equals("()"))
						throw new NoSuchPathException("The path does not exist");
					sql+="AND E.path_id in " + inpath +")\n";
				} 
				break;
				
			case EdgeConstant.PRECEDING_SIBLING:		//preceding-sibling
				System.out.println( "EdgeTranslator.processAxis(), step: PRECEDING-SIBLING");	//klarinda 20060824
				//20060906 add docid
				sql = ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";
				sql+= "    SELECT DISTINCT E.docid, E.id, E.parent_id, E.end_desc_id \n";
				sql+= "    FROM Edge E, Q"+(_noOfTables-1)+" Q \n";
				sql+= "    WHERE E.docid = Q.docid AND E.end_desc_id < Q.id AND E.parent_id = Q.parent_id ";
				_currentPath.removeLast();
				_currentPath.add(step.getNameTest());
				if (step.getNameTest().compareTo("*") == 0 || step.getNameTest().compareTo("node()") == 0){
					sql+=")\n";
				}
				else {
					String inpath = _currentPath.getActualPath();
					if (inpath.equals("()"))
						throw new NoSuchPathException("The path does not exist");
					sql+="AND E.path_id in " + inpath +")\n";
				} 
				break;
				
			default:
				System.out.println("Type:" + step.getType());
		}
		
		_sqlQuery += sql;
		System.out.println( "EdgeTranslator.processAxis(), sql:\n" +sql);		//klarinda 20060825
	}
	
	void processPredicate(Step step) throws NoSuchPathException {
		int n_from = 0, n_to = 0;
		//int nf,nt;
		
		System.out.println( "EdgeTranslator.processPredicate()");		//klarinda 20060824
		
		_noOfTables++;
		
		String sql = "";
		//String selectfromsql = "";		//20070625 to handle multiple AND/OR
		String selectsql = "";			//20070626 to handle multiple AND/OR
		String fromsql = "";			//20070626 to handle multiple AND/OR
		
		if(!_isPrefetching)				//20070626
		selectsql = ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";
		
		//20070625 add for loop and change Step class
		for (int i = 0; i < step.getPredicate().length; i++) {
		if (step.hasEquality(i)) {

		//if (step.hasEquality()){ 20070625 add for loop
			CurrentPath tempPath = new CurrentPath(_dbConnection);
			
			tempPath.addString(_currentPath.getPathString());
			//tempPath.add(step.getPredicate(i));
			
			//for predicate > 1 steps (klarinda 20070625) 
			String[] predicateStep = step.getPredicate(i).split("/");
			if (predicateStep.length > 1) {
				for(int x=0; x<predicateStep.length; x++) 
					tempPath.add(predicateStep[x].trim());
			} else { 
				tempPath.add(step.getPredicate(i));
			}

			String inpath = tempPath.getActualPath();
			if (inpath.equals("()"))
				throw new NoSuchPathException("The path does not exist");
			
			//20060906 add docid
			//20070625 add selectfromsql
			//sql+= ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";	
			if (i==0) {
				selectsql+= "    SELECT DISTINCT Q.docid, Q.id, Q.parent_id, Q.end_desc_id \n";
				fromsql  += "    FROM Q"+(_noOfTables-1)+" Q, ";
				sql += "    WHERE ";
			}
			else {
				fromsql+= ", ";
				if(step.getAndExpr(i))
					sql += "    AND ";
				else if ( step.getOrExpr(i) )
					sql += "    OR ";
			}
			for (int par=0; par<step.getNoOfOpenParenthesis(i); par++)
				sql += "( ";
			
			//attribute table is separated from elements
			if(step.getPredicate(i).charAt(0) == '@') 		//attribute 
				fromsql+= "Attribute E"+i;
			else											//element
				fromsql+= "Edge E"+i;
			
			if(predicateStep.length == 1)	//20070625
				sql+= "E"+i+".docid = Q.docid "
				   + "AND E"+i+".parent_id = Q.id "
				   + "AND E"+i+".path_id in " + inpath +"\n";
			else //20070625 to handle predicate > 1 step -> use descendant
				sql+= "E"+i+".docid = Q.docid "
				   + "AND E"+i+".id >= Q.id "
				   + "AND E"+i+".id <= Q.end_desc_id "
				   + "AND E"+i+".path_id in " + inpath +"\n";
			
			//klarinda 20070726: JOIN
			/* it is a join if and only if
			 * 		- '=' or '!='
			 * 		- doesn't start with quote (note: double quote has been converted to quote)
			 * 		- is not a number
			 * ex1: /bib / * [author/first = /bib/ * /author/last]
			 * ex2: /bib / * [author/first = author/last] 
			 * (pls note that the 2 XPath are different)
			 */
			boolean isJoin = false;
			if ((step.getEqualityOperator(i).equals("=") || step.getEqualityOperator(i).equals("!="))
					&& step.getEquality(i).charAt(0) != '\'') 
			{
				try {
					Float.valueOf(step.getEquality(i));
				} catch(NumberFormatException e) {
					//it is a join (fulfill the 3 conditions above)
					isJoin = true;
					
					boolean isSlash = false;	//ex2
					boolean isJoinAttribute = false;

					if(step.getEquality(i).charAt(0) == '/') 
						isSlash = true;			//ex1
					CurrentPath joinPath = new CurrentPath(_dbConnection);
					if(!isSlash)
						joinPath.addString(_currentPath.getPathString());
					String[] joinStep = step.getEquality(i).split("/");
					for(int x=0; x<joinStep.length; x++) {
						joinPath.add(joinStep[x].trim());
					}
					
					if (joinStep[joinStep.length-1].trim().charAt(0) == '@')
						isJoinAttribute = true;
					
					String joinPathId = joinPath.getActualPath();
					if (joinPathId.equals("()")) 
						throw new NoSuchPathException("The join path does not exist");
		
					if(isJoinAttribute) { //attribute table is separated from elements	
						fromsql += ", Attribute E"+i+"j";
					} else {
						fromsql += ", Edge E"+i+"j";
					}
					sql += "      AND E"+i+".value like E"+i+"j.value  AND E"+i+"j.path_id in "+joinPathId+"\n";
					if (!isSlash) {
						if (joinStep.length == 1)//child axes
							sql += "      AND E"+i+"j.docid = Q.docid AND E"+i+"j.parent_id = Q.id ";
						else 
							sql += "      AND E"+i+"j.docid = Q.docid AND E"+i+"j.id >= Q.id AND E"+i+"j.id <= Q.end_desc_id ";
					}
				}
			} 
			
			if (!isJoin) { //not join 
				//20070625 for MINT QUERY
				if (step.getEqualityOperator(i).equals("!!!")) {	
					sql+= " \n";
				} else
				if (step.getEqualityOperator(i).equals("=")) {	
					sql+= "          AND E"+i+".value like "+ step.getEquality(i)+" \n";
				} else if (step.getEqualityOperator(i).equals("!=")) {	
					sql+= "          AND E"+i+".value not like "+ step.getEquality(i)+" \n";
				} else {
					//problem: sometimes the column can't be casted to float. SQL will throw error
					try {
						Float temp = Float.valueOf(step.getEquality(i));
						if (temp == null) throw new NoSuchPathException("Predicate value incorrect");
					} catch(NumberFormatException e) {
						 throw new NoSuchPathException("Precicate value incorrect");
					}
					sql+= "          AND CAST(E"+i+".value AS FLOAT) "+ step.getEqualityOperator(i) + " " + step.getEquality(i)+" \n";
				}
			}	//end of else (not join)
			
			for (int par=0; par<step.getNoOfCloseParenthesis(i); par++)
				sql += " ) ";
		}
		else {
			n_from = step.getPredicateFrom(i);
			n_to = step.getPredicateTo(i);
			//20060906 add docid
			//sql = ",Q"+_noOfTables+" (docid, id, parent_id, end_desc_id) AS ( \n";
			if (step.getPredicate().length==1) {	//the only condition is position predicate
	 			selectsql+= "    SELECT DISTINCT T.docid, T.id, T.parent_id, T.end_desc_id \n";
	 			fromsql  += "	 FROM ";
			} else { //multiple conditions
				if (i==0) {
					selectsql+= "    SELECT DISTINCT Q.docid, Q.id, Q.parent_id, Q.end_desc_id \n";
					fromsql  += "    FROM Q"+(_noOfTables-1)+" Q, \n	    ";
					sql += "    WHERE ";
				}
				else {
					fromsql  += ", \n	    ";
					if(step.getAndExpr(i))
						sql += "    AND ";
					else if ( step.getOrExpr(i) )
						sql += "    OR ";
				}
				for (int par=0; par<step.getNoOfOpenParenthesis(i); par++)
					sql += "( ";
			}
 			fromsql+= "(SELECT Q.docid, Q.id, Q.parent_id, Q.end_desc_id, \n";
 			if(step.getType() == EdgeConstant.CHILD || step.getType() == EdgeConstant.FOLLOWING_SIBLING)
 				fromsql+= "            RANK() OVER (PARTITION BY Q.parent_id ORDER BY Q.docid, Q.id) pos \n";
 			else if(step.getType() == EdgeConstant.PRECEDING_SIBLING)
 				fromsql+= "            RANK() OVER (PARTITION BY Q.parent_id ORDER BY Q.docid, Q.id DESC) pos \n";
 			else if(step.getType() == EdgeConstant.FOLLOWING)
 				fromsql+= "            RANK() OVER (ORDER BY Q.docid, Q.id) pos \n";
 			else if(step.getType() == EdgeConstant.PRECEDING)
 				fromsql+= "            RANK() OVER (ORDER BY Q.docid, Q.id DESC) pos \n";
			if (step.getPredicate().length==1) {	//the only condition is position predicate
	 			fromsql+= "            FROM Q"+ (_noOfTables-1) +" Q) as T \n    ";
	 			if(n_from==n_to)
	 				sql+= "    WHERE T.pos="+n_from+" \n";
	 			else 
	 				sql+= "    WHERE T.pos>="+n_from+" AND T.pos<="+n_to+" \n";
			} else {	//multiple conditions (AND/OR)
	 			fromsql+= "            FROM Q"+ (_noOfTables-1) +" Q) as T"+i+" \n    ";
	 			if(n_from==n_to)
	 				sql+= "T"+i+".pos="+n_from;
	 			else 
	 				sql+= "T"+i+".pos>="+n_from+" AND T"+i+".pos<="+n_to;
	 			sql+= " AND T"+i+".id = Q.id AND T"+i+".docid = Q.docid \n";
				for (int par=0; par<step.getNoOfCloseParenthesis(i); par++)
					sql += " ) ";
			}
		}
		
		}	//end of for loop (20070625)
		
	
		_sqlQuery += selectsql + fromsql +"\n"+ sql + "    )\n";
		System.out.println( "EdgeTranslator.processPredicate(), sql:\n"+sql);		//klarinda 20060825
	}
	
	
}
